import Router from 'koa-router'
import _ from 'lodash'
import R from 'ramda'
import glob from 'glob'
import { resolve } from 'path'

// 上传图片 multer
import upload from './upload'

let logTimes = 0

// Map结构提供了“值—值”的对应，是一种更完善的Hash结构实现。如果你需要“键值对”的数据结构，Map比Object更合适。它类似于对象，也是键值对的集合，但是“键”的范围不限于字符串，各种类型的值（包括对象）都可以当作键。
export let routersMap = new Map()

// Symbol，表示独一无二的值。
export const symbolPrefix = Symbol('prefix')

// 转换成[value]  //get post 在接口名一样..会在同一个数组里[ function GET , function POST]
export const isArray = v => _.isArray(v) ? v : [v]

// 返回布尔值，表示参数字符串是否在原字符串的头部。   startsWith()：返回布尔值，表示参数字符串是否在原字符串的头部。
export const normalizePath = path => path.startsWith('/') ? path : `/${path}`

export default class Route {
  constructor(app, apiPath) {
    this.app = app
    this.router = new Router()
    this.apiPath = apiPath
  }

  init() {
    glob.sync(resolve(this.apiPath, './*.js')).forEach(require)  //同步获取目录下的js文件,省下一个一个require文件

    // Maps(映射)
    for (let [conf, controller] of routersMap) {
      const controllers = isArray(controller)
      let prefixPath = conf.target[symbolPrefix]
      if (prefixPath) prefixPath = normalizePath(prefixPath)
      const routerPath = prefixPath + conf.path

      // let middleware = [
      //   async (ctx, next) => {
      //     ctx.body="xixi"
      //     ctx.gogogo = 'gogogo'
      //     await next()
      //   },
      //   (ctx,next) => {
      //     ctx.body = ctx.gogogo
      //   }
      // ]
      //...middleware 通过扩展运算符解构数组 [中间件1(ctx, next)=>{ 执行函数,跳到下个中间件2 await next()}, 中间件2(ctx, next)=> {}]
      // 中间件1的ctx.属性值.  可以在中间件2使用. 同一个使用域
      // this.router.get('/xixi', ...middleware)
      this.router[conf.method](routerPath, ...controllers)
    }

    // 图片上传接口
    this.router.post('/shop/upload', upload.single('file'), async (ctx) => {
      console.log(ctx.req.file)
      let file = ctx.req.file
      if (!file) return (ctx.body = {
        success: false,
        error: '上传失败'
      })

      let imgPath = file.path.replace('static', '')
      
      ctx.body = {
        success: true,
        data: imgPath
      }
    })

    
    this.app.use(this.router.routes())
    this.app.use(this.router.allowedMethods())
  }
}
export const router = conf => (target, key, desc) => {
  // target 是class wehcatController的类 
  conf.path = normalizePath(conf.path)
 
  // Map { key索引:{ target: wechatController {}, method: 'get', path: '/wechat-hear' } => 键值 : [ [Function], [Function: wechatHear] ] }
  routersMap.set({
    target: target,
    ...conf
  }, target[key])
}
// function controller (path) { return function(target) { return target.prototype[symbolPrefix] = path }}  箭头函数柯里化
export const controller = path => target => target.prototype[symbolPrefix] = path

// 给routerMap set 路由值
export const get = path => router({
  method: 'get',
  path: path
})

export const post = path => router({
  method: 'post',
  path: path
})

export const put = path => router({
  method: 'put',
  path: path
})

export const del = path => router({
  method: 'del',
  path: path
})

const decorate = (args, middleware) => {
  let [target, key, descriptor] = args
  target[key] = isArray(target[key])
  target[key].unshift(middleware)
  return descriptor
}

export const convert = middleware => (...args) => decorate(args, middleware)

export const required = rules => convert(async (ctx, next) => {
  let errors = []

  const passRules = R.forEachObjIndexed(
    (value, key) => {
      errors = R.filter(i => !R.has(i, ctx.request[key]))(value)
    }
  )
  
  // rules: { query: [ 'code' ] }
  passRules(rules)

  if(errors.length) ctx.throw(412, `${errors.join(', ')}参数缺失`)

  await next()
})


export const Log = convert(async (ctx, next) => {
  logTimes++;
  console.log(`${logTimes}: ${ctx.method} - ${ctx.url}`);
  console.time(`${logTimes}: ${ctx.method} - ${ctx.url}`);
  await next();
})